package registry

import (
	"context"
	"fmt"
	"log/slog"

	clientv3 "go.etcd.io/etcd/client/v3"
)

// ServiceRegister 代表服务注册器
type ServiceRegister struct {
	client   *clientv3.Client   // ETCD 客户端
	leaseID  clientv3.LeaseID   // 租约ID
	leaseTTL int64              // 租约TTL
	service  *Service           // 服务信息
	ctx      context.Context    // 上下文
	cancel   context.CancelFunc // 取消函数
}

// NewServiceRegister 创建一个新的服务注册器实例
func NewServiceRegister(client *clientv3.Client, service *Service) *ServiceRegister {
	ctx, cancel := context.WithCancel(context.Background())
	return &ServiceRegister{
		client:   client,
		leaseTTL: 5,
		service:  service,
		ctx:      ctx,
		cancel:   cancel,
	}
}

// Register 注册服务
func (sr *ServiceRegister) Register() error {
	err := sr.register()
	if err != nil {
		slog.Error("注册服务失败", slog.String("error", err.Error()))
		return err
	}

	slog.Info("服务注册成功", slog.String("service key", sr.getServiceKey()))
	return err
}

// register 执行服务注册的具体逻辑
func (sr *ServiceRegister) register() error {
	// 首先检查服务是否已经存在
	getResp, err := sr.client.Get(sr.ctx, sr.getServiceKey())
	if err != nil {
		return fmt.Errorf("check existing service failed: %w", err)
	}
	if len(getResp.Kvs) > 0 {
		slog.Info("服务已注册", slog.String("service key", sr.getServiceKey()))
		return fmt.Errorf("service already registered")
	}

	// 申请一个租约
	resp, err := sr.client.Grant(sr.ctx, sr.leaseTTL)
	if err != nil {
		return fmt.Errorf("grant lease failed: %w", err)
	}
	sr.leaseID = resp.ID
	slog.Info("租约申请成功", slog.String("leaseID", fmt.Sprintf("%d", sr.leaseID)))

	// 将服务信息序列化为JSON格式
	serviceInfo, err := sr.service.ToJSON()
	if err != nil {
		return fmt.Errorf("marshal service failed: %w", err)
	}

	// 将服务信息放入etcd中，并绑定租约
	_, err = sr.client.Put(sr.ctx, sr.getServiceKey(), serviceInfo, clientv3.WithLease(sr.leaseID))
	if err != nil {
		return fmt.Errorf("put service failed: %w", err)
	}
	slog.Info("服务信息已放入etcd", slog.String("service key", sr.getServiceKey()))

	// 启动一个goroutine来保持租约的存活
	go sr.keepAlive()

	return nil
}

// keepAlive 保持租约的存活
func (sr *ServiceRegister) keepAlive() {
	// 开始租约续租
	keepAliveChan, err := sr.client.KeepAlive(sr.ctx, sr.leaseID)
	if err != nil {
		slog.Error("启动租约续租失败", slog.String("error", err.Error()))
		return
	}
	slog.Info("租约续租已启动", slog.String("leaseID", fmt.Sprintf("%d", sr.leaseID)))

	for {
		select {
		case <-sr.ctx.Done():
			slog.Info("上下文已取消，停止续租")
			return
		case resp, ok := <-keepAliveChan:
			if !ok || resp == nil {
				slog.Warn("续租通道已关闭，尝试重新注册")
				if err := sr.Register(); err != nil {
					slog.Error("重新注册失败", slog.String("error", err.Error()))
				}
				return
			}
		}
	}
}

// Deregister 注销服务
func (sr *ServiceRegister) Deregister() error {
	// 取消上下文，停止续租
	sr.cancel()
	slog.Info("上下文已取消，停止租约续租")

	// 从etcd中删除服务信息
	_, err := sr.client.Delete(context.Background(), sr.getServiceKey())
	if err != nil {
		slog.Error("从etcd中删除服务失败", slog.String("error", err.Error()))
		return fmt.Errorf("delete service failed: %w", err)
	}
	slog.Info("服务已从etcd中删除", slog.String("service key", sr.getServiceKey()))

	// 关闭etcd客户端
	err = sr.client.Close()
	if err != nil {
		slog.Error("关闭etcd客户端失败", slog.String("error", err.Error()))
		return err
	}
	slog.Info("etcd客户端已成功关闭")
	return nil
}

// getServiceKey 获取服务在etcd中的键
func (sr *ServiceRegister) getServiceKey() string {
	return fmt.Sprintf("/%s/%s/%s", sr.service.Project, sr.service.Name, sr.service.Address)
}
